
"""
假设一个程序的运行时间大约60s左右，为了减少判断性能开销，怎样用较少的轮询次数来判断这个程序已经结束。
例如有一个U型的贝塔曲线，横坐标是总时间，纵坐标是轮询间隔时间，U型的底部对应的应该正好是60s的位置，假设我们轮询5次那间隔时间应该是[1s, 0.5s,0.25s,0.5s,1s]这样的形式。
我们创建一个函数generate_intervals，接受三个参数，total_time，min_interval，num_intervals，返回一个轮询间隔时间的列表。
total_time = 60 # 总时间
min_interval = 0.25 # 最小间隔时间
num_intervals = 5 # 时间间隔个数
注意设置最小间隔时间，要保证总时间不变，或许可以用积分来解决总时间不变的问题，请用python代码来实现。
缺点：总时间无法控制 
"""
# In[1]
import numpy as np
import matplotlib.pyplot as plt
import time

# 输出成图像,横坐标为正整数，在对应位置绘制竖线，没有纵坐标
def plot_line(list):
    # 设置图像大小
    plt.figure(figsize=(10, 1), dpi=100)
    # 绘制竖线
    plt.vlines(list, 0, 1)
    # 设置横坐标
    plt.xticks(list)
    # 设置纵坐标
    plt.yticks([])
    # 显示图像
    plt.show()

# 生成轮询间隔时间的列表
def generate_intervals(total_time, min_interval, num_intervals, std):
    if num_intervals == 1:
        return [total_time]
    # 延长间隔
    def extend_gaps(a, n):
        new_a = []
        offset = 0
        for i in a:
            if i < n:
                offset = offset + n - i
                new_a.append(n)
            else:
                new_a.append(i)
            if offset > 0 and new_a[-1] > n:
                if offset > new_a[-1] - n:
                    offset = offset - new_a[-1] + n
                    new_a[-1] = n
                else:
                    new_a[-1] = new_a[-1] - offset
                    offset = 0
        return new_a
    def normal_distribution(x, mean, std_dev):
        return np.exp(-.5 * np.power((x - mean) / std_dev, 2.))
    x = np.linspace(0, total_time*2, num_intervals)
    y = normal_distribution(x, total_time, std)
    # print(y)
    intervals = 1 / y
    intervals = intervals / np.sum(intervals) * (2 * total_time)
    # print('原始间隔时间', np.round(intervals, 3))
    min_time = min(intervals)
    if min_time >= min_interval:
        intervals[num_intervals//2] = min_interval
    else:
        a = intervals[num_intervals//2:]
        b = intervals[:num_intervals//2]
        b = b[::-1]
        a = extend_gaps(a, min_interval)
        b = extend_gaps(b, min_interval)
        intervals = np.concatenate((b[::-1], a))
    return intervals

# 测试函数
total_time = 0.8 # 总时间
min_interval = 0.02 # 最短间隔时间
num_intervals = 18 # 时间间隔个数
std = total_time * 0.8 # 标准差，乘数区间为[0, 1]，数越大，间隔越均匀
start = time.time()
intervals = generate_intervals(total_time, min_interval, num_intervals, std)


# 输出
np.set_printoptions(suppress=True)
print('间隔时间', np.round(intervals, 3))
# 计算时间间隔，显示从0开始的实际时间点
interval_list = [0]
n = 0
for interval in intervals:
    n = n + interval
    interval_list.append(n)
# 计算中位数
print('延迟中心点：', np.round(interval_list[num_intervals//2], 3))
print('实际时间点：', np.round(interval_list, 3))
print('生成时间', time.time()-start, 's')
# 输出成图像
plot_line(interval_list)

# 测试轮询
start = time.time()
offset = 0
for i in range(num_intervals):
    t = intervals[i] + offset
    t = t if t > 0 else 0
    time.sleep(t)
    t = time.time()-start
    print('轮询', i+1, '次,', '偏移', np.round((t)-sum(intervals[:i+1]), 3), '秒')
    offset = sum(intervals[:i+1])-(t)
    if t > total_time + 2:
        break

# In[2]